package com.css.fxfzmh.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * Created by lixiaofeng on 2020/4/22.
 */

public class PlatformBeanUtils {
    // private static Pattern linePattern = Pattern.compile("_(\\w)");

    private static int modifierInt = 0x0;

    static {
        //包含以下修饰符的字段将不进行转换
        modifierInt = modifierFromString("final","static");
    }

    public static List copyProperties(List<Map<String, Object>> sourceList, Class toCls) {
        List targetList = new LinkedList();
        if (PlatformObjectUtils.isNotEmpty(sourceList)) {
            for (Map<String, Object> stringObjectMap : sourceList) {
                targetList.add(copyProperties(stringObjectMap, toCls));
            }
        }
        return targetList;
    }

    public static Object copyProperties(Map<String, Object> map, Class toCls) {
        Object toObj = null;
        try {
            if (!PlatformObjectUtils.isEmpty(map)) {
                toObj = toCls.newInstance();
                Field[] fields = getAllFields(toCls);
                Map<String, Object> tempMap = new HashMap();


                //支持数据库返回列名为下划线格式
                for (Map.Entry<String, Object> entry : map.entrySet()) {

                    String key = entry.getKey();
                    //统一转成小写
                    key = key.toLowerCase().replace("_", "");
                    tempMap.put(key, entry.getValue());

                    //如果有下划线，转成驼峰
//                    if (key.split("_").length > 1) {
//                        key = key.toLowerCase();
//                        Matcher matcher = linePattern.matcher(key);
//                        StringBuffer sb = new StringBuffer();
//                        while (matcher.find()) {
//                            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
//                        }
//                        key = matcher.appendTail(sb).toString();
//                        tempMap.put(key,entry.getValue());
//                    }else {
//                        tempMap.put(key,entry.getValue());
//                    }
                }
                map = tempMap;

                for (Field field : fields) {

                    //根据修饰符判断此字段是否需要进行转换
                    if (!isNeedConvert(field)) {
                        continue;
                    }

                    String fieldName = field.getName();
                    //将字段名转成全小写
                    String lowerFieldName = fieldName.toLowerCase();
                    Object value = map.get(lowerFieldName);
                    String setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    //如果类型为boolean，字段名称为isXxx,生成的set方法名称为setXxx
                    if (field.getType().getName().equals("boolean")) {
                        if ("is".equals(fieldName.substring(0, 2))) {
                            String fieldNameForSetMethod = fieldName.substring(2);
                            setMethodName = "set" + fieldNameForSetMethod.substring(0, 1).toUpperCase() + fieldNameForSetMethod.substring(1);
                        }
                    }


                    Method setMethod = toCls.getMethod(setMethodName, field.getType());


                    if (field.getType().getName().equals("short")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            value = 0;
                        }
                        setMethod.invoke(toObj, Short.valueOf(String.valueOf(value)));
                    } else if (field.getType().getName().equals("int")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            value = 0;
                        }
                        setMethod.invoke(toObj, Integer.valueOf(String.valueOf(value)));
                    } else if (field.getType().getName().equals("long")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            value = 0;
                        }
                        setMethod.invoke(toObj, Long.valueOf(String.valueOf(value)));
                    }else if (field.getType().getName().equals("float")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            value = 0;
                        }
                        setMethod.invoke(toObj, Float.valueOf(String.valueOf(value)));
                    }else if (field.getType().getName().equals("double")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            value = 0;
                        }
                        setMethod.invoke(toObj, Double.valueOf(String.valueOf(value)));
                    }else if (field.getType().getName().equals("boolean")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            continue;
                        }
                        if ("0".equals(String.valueOf(value))) {
                            value = false;
                        } else if ("1".equals(String.valueOf(value))) {
                            value = true;
                        }
                        setMethod.invoke(toObj, value);
                    } else if (field.getType().getName().equals("java.sql.Timestamp")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            continue;
                        }
                        setMethod.invoke(toObj, value);
                    } else if (field.getType().getName().equals("java.util.Date")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            continue;
                        }
                        setMethod.invoke(toObj, value);
                    } else if (field.getType().getName().equals("java.lang.Short")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            continue;
                        }
                        setMethod.invoke(toObj, Short.valueOf(String.valueOf(value)));
                    } else if (field.getType().getName().equals("java.lang.Integer")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            continue;
                        }
                        setMethod.invoke(toObj, Integer.valueOf(String.valueOf(value)));
                    } else if (field.getType().getName().equals("java.lang.Long")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            continue;
                        }
                        setMethod.invoke(toObj, Long.valueOf(String.valueOf(value)));
                    }else if (field.getType().getName().equals("java.lang.Float")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            value = 0;
                        }
                        setMethod.invoke(toObj, Float.valueOf(String.valueOf(value)));
                    }else if (field.getType().getName().equals("java.lang.Double")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            value = 0;
                        }
                        setMethod.invoke(toObj, Double.valueOf(String.valueOf(value)));
                    } else if (field.getType().getName().equals("java.lang.Boolean")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            continue;
                        }
                        if ("0".equals(String.valueOf(value))) {
                            value = false;
                        } else if ("1".equals(String.valueOf(value))) {
                            value = true;
                        }
                        setMethod.invoke(toObj, value);
                    } else if (field.getType().getName().equals("java.lang.String")) {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            continue;
                        }
                        setMethod.invoke(toObj, String.valueOf(value));
                    } else {
                        if (PlatformObjectUtils.isEmpty(value)) {
                            continue;
                        } else {
                            setMethod.invoke(toObj, value);
                        }
                    }

                }
            }

        } catch (InstantiationException e) {
            throw new RuntimeException("Failed to copyProperties : InstantiationException", e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Failed to copyProperties : IllegalAccessException", e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("Failed to copyProperties : NoSuchMethodException", e);
        } catch (SecurityException e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (InvocationTargetException e) {
            throw new RuntimeException("Failed to copyProperties : InvocationTargetException", e);
        }

        return toObj;
    }


    public static Field[] getAllFields(Class clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }

    /**
     * 根据字段修饰符判断该字段是否需要转换
     * @param field
     * @return
     */
    private static boolean isNeedConvert(Field field){

        int fieldModifiers = field.getModifiers();
        if ((fieldModifiers & modifierInt) != 0) {
            return false;
        }
        return true;
    }


    /**
     * 根据修饰符返回对应的整形
     * @param modifierStrArr
     * @return
     */
    private static int modifierFromString(String... modifierStrArr) {

        int m = 0x0;
        for (int i = 0; i < modifierStrArr.length; i++) {
            String s = modifierStrArr[i];
            if ("public".equals(s)){
                m |= Modifier.PUBLIC;
            } else if ("protected".equals(s)){
                m |= Modifier.PROTECTED;
            } else if ("private".equals(s)){
                m |= Modifier.PRIVATE;
            } else if ("static".equals(s)){
                m |= Modifier.STATIC;
            } else if ("final".equals(s)){
                m |= Modifier.FINAL;
            } else if ("transient".equals(s)){
                m |= Modifier.TRANSIENT;
            } else if ("volatile".equals(s)){
                m |= Modifier.VOLATILE;
            }
        }

        return m;
    }


    //------------------------------------------------------------暂时无用方法---------------------
    public static Map<String, Object> copyProperties(Object fromObj) throws Exception {

        Map<String, Object> map = new HashMap<String, Object>();
        Class fromCls = fromObj.getClass();
        try {
            Field[] fields = fromCls.getDeclaredFields();
            for (Field field : fields) {
                String fieldName = field.getName();
                String getMethodName = "get"
                        + fieldName.substring(0, 1).toUpperCase()
                        + fieldName.substring(1);
                Method getMethod = fromCls.getMethod(getMethodName);
                map.put(fieldName, getMethod.invoke(fromObj));
            }

        } catch (IllegalAccessException e) {
            throw e;
        } catch (NoSuchMethodException e) {
            throw e;
        } catch (SecurityException e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (InvocationTargetException e) {
            throw e;
        }
        return map;
    }

    public static Map<String, Object> objectToMap(Object obj) {
        if (obj == null) {
            return null;
        }

        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Field[] declaredFields = obj.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(obj));
            }
        } catch (Exception e) {

        }

        return map;
    }

}
